package middlewares

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"minititok/Entities"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

var (
	logFile  *os.File
	initOnce sync.Once
)

type LogEntry struct {
	Timestamp    string `json:"timestamp"`
	Level        string `json:"level"`
	Method       string `json:"method"`
	Path         string `json:"path"`
	Query        string `json:"query"`
	IP           string `json:"ip"`
	User         string `json:"user"`
	Status       int    `json:"status"`
	Duration     string `json:"duration"`
	RequestBody  string `json:"request_body"`
	ErrorMessage string `json:"error,omitempty"`
}

func getLogFilename() string {
	filename := time.Now().Format(time.RFC3339)
	filename = strings.ReplaceAll(filename, ":", "-")
	filename = strings.ReplaceAll(filename, "+", "_")
	return filename + ".log"
}

func Logger() gin.HandlerFunc {
	initOnce.Do(func() {
		logDir := "./staticSrc/log"
		err := os.MkdirAll(logDir, os.ModePerm)
		if err != nil {
			panic("无法创建日志目录: " + err.Error())
		}
		filename := getLogFilename()
		logPath := filepath.Join(logDir, filename)
		logFile, err = os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			panic("日志文件创建失败: " + err.Error())
		}
	})

	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		method := c.Request.Method
		ip := c.ClientIP()

		var requestBody string
		if c.Request.Body != nil {
			buf, _ := io.ReadAll(c.Request.Body)
			requestBody = string(buf)
			c.Request.Body = io.NopCloser(bytes.NewBuffer(buf))
		}

		if path == "/login/" || path == "/register/" {
			var bodyMap map[string]interface{}
			if err := json.Unmarshal([]byte(requestBody), &bodyMap); err == nil {
				// 删除password字段
				delete(bodyMap, "password")
				// 重新序列化
				filtered, err := json.Marshal(bodyMap)
				if err == nil {
					requestBody = string(filtered)
				}
			}
		}

		c.Next()

		status := c.Writer.Status()
		duration := time.Since(start)

		user, ok := c.Get("user")
		username := "unKnowUser"
		if ok {
			if u, ok := user.(*Entities.User); ok {
				username = u.Username
			}
		}

		errMsg := ""
		if len(c.Errors) > 0 {
			errMsg = c.Errors.String()
		}

		entry := LogEntry{
			Timestamp:    start.Format(time.RFC3339),
			Level:        "INFO",
			Method:       method,
			Path:         path,
			Query:        query,
			IP:           ip,
			User:         username,
			Status:       status,
			Duration:     fmt.Sprintf("%dms", duration.Milliseconds()),
			RequestBody:  requestBody,
			ErrorMessage: errMsg,
		}
		if errMsg != "" {
			entry.Level = "ERROR"
		}

		data, _ := json.Marshal(entry)
		data, _ = json.MarshalIndent(entry, "", "  ")
		logFile.WriteString(string(data) + "\r\n")
	}
}
